import tkinter as tk
from tkinter import filedialog, messagebox
import numpy as np
from PIL import Image, ImageTk
import logging
import json
import os
import subprocess
import sys
import config
import image_io
import image_channels
import image_transform
import cv2  # 引入OpenCV库

class ImageProcessor:
    def __init__(self, master):
        """初始化ImageProcessor类的实例"""
        self.master = master  # 主窗口
        self.current_language = self.load_language_data()["current_language"]  # 从JSON文件加载默认语言
        master.title(self.language_data[self.current_language]['app_title'])  # 设置初始窗口标题

        self.image = None  # 用于存储当前图像的numpy数组
        self.image_path = ""  # 用于存储当前图像的文件路径
        self.rect_start_x = None  # 用于存储裁剪矩形框起始点的x坐标
        self.rect_start_y = None  # 用于存储裁剪矩形框起始点的y坐标
        self.rect_end_x = None  # 用于存储裁剪矩形框结束点的x坐标
        self.rect_end_y = None  # 用于存储裁剪矩形框结束点的y坐标
        self.is_drawing = False  # 用于标记是否正在绘制裁剪矩形框
        self.current_rect = None  # 用于存储当前绘制的裁剪矩形框对象

        self.create_widgets()  # 创建GUI组件
        self.add_threshold_menu()
        self.add_edge_menu()  # 添加边缘菜单
        self.add_mask_menu()  # 添加掩膜菜单
        self.add_bitwise_menu()  # 添加位运算菜单

    def load_language_data(self):
        """从language.json文件加载语言数据"""
        try:
            with open('language.json', 'r', encoding='utf-8') as f:
                return json.load(f)
        except FileNotFoundError:
            logging.error("language.json文件未找到！")
            return {"current_language": "en", "en": {"app_title": "Image Processing App"}}
        except json.JSONDecodeError:
            logging.error("language.json文件格式错误！")
            return {"current_language": "en", "en": {"app_title": "Image Processing App"}}

    def save_language_data(self, data):
        """保存语言数据到language.json文件"""
        try:
            with open('language.json', 'w', encoding='utf-8') as f:
                json.dump(data, f, ensure_ascii=False, indent=4)
        except Exception as e:
            logging.error(f"保存语言设置失败: {e}")

    @property
    def language_data(self):
        """获取当前语言数据的属性"""
        return self.load_language_data()

    def create_widgets(self):
        """创建GUI组件"""
        # 创建菜单栏
        self.menubar = tk.Menu(self.master)  # 保存对menubar的引用
        self.master.config(menu=self.menubar)

        # 创建文件菜单
        file_menu = tk.Menu(self.menubar)
        file_menu.add_command(label=self.language_data[self.current_language]['open_original'], command=self.Open_as_original)
        file_menu.add_command(label=self.language_data[self.current_language]['open_grayscale'], command=self.Open_as_gray)
        file_menu.add_command(label=self.language_data[self.current_language]['merge_channels'], command=self.merge_channels)
        file_menu.add_command(label=self.language_data[self.current_language]['save_image'], command=self.save_image)
        file_menu.add_separator()

        # 创建一个菜单项,用于切换语言,它应该有两个子菜单项,分别用于切换中文和英文。当前语言用一个勾选标记来表示。
        language_menu = tk.Menu(file_menu)
        self.language_var = tk.StringVar(value=self.current_language)
        language_menu.add_radiobutton(label="中文", variable=self.language_var, value="zh", command=self.change_language)
        language_menu.add_radiobutton(label="English", variable=self.language_var, value="en", command=self.change_language)
        file_menu.add_cascade(label=self.language_data[self.current_language]['language'], menu=language_menu)

        file_menu.add_command(label=self.language_data[self.current_language]['exit'], command=self.master.quit)
        self.menubar.add_cascade(label=self.language_data[self.current_language]['file'], menu=file_menu)

        # 创建信息菜单
        info_menu = tk.Menu(self.menubar)
        info_menu.add_command(label=self.language_data[self.current_language]['details'], command=self.show_image_details)
        self.menubar.add_cascade(label=self.language_data[self.current_language]['info'], menu=info_menu)

        # 创建编辑菜单
        edit_menu = tk.Menu(self.menubar)
        edit_menu.add_command(label=self.language_data[self.current_language]['crop'], command=self.crop_image)
        edit_menu.add_command(label=self.language_data[self.current_language]['reset'], command=self.reset_image)
        # 添加亮度调节选项
        edit_menu.add_command(label=self.language_data[self.current_language]['adjust_brightness'], command=self.adjust_brightness)
        self.menubar.add_cascade(label=self.language_data[self.current_language]['edit'], menu=edit_menu)
        # 创建图像分割菜单
        split_menu = tk.Menu(self.menubar)
        self.menubar.add_cascade(label=self.language_data[self.current_language]['split'], menu=split_menu)
        split_menu.add_command(label=self.language_data[self.current_language]['split_channels'], command=self.split_image_channels)

        # 创建图像显示区域
        self.canvas = tk.Canvas(self.master, width=600, height=400)
        self.canvas.pack()

        self.canvas.bind("<Button-1>", self.on_mouse_down)
        self.canvas.bind("<B1-Motion>", self.on_mouse_move)
        self.canvas.bind("<ButtonRelease-1>", self.on_mouse_up)

    def change_language(self):
        """切换语言"""
        # 获取所选择的语言
        self.current_language = self.language_var.get()

        # 更新language.json文件中的current_language
        language_data = self.language_data
        language_data["current_language"] = self.current_language
        self.save_language_data(language_data)

        logging.info(f"当前语言已切换为{self.current_language}。")
        confirm = messagebox.askyesno(
            self.language_data[self.current_language]['confirm_restart'],
            self.language_data[self.current_language]['restart_warning']
        )
        if confirm:
            self.master.destroy()
            # os.execl(sys.executable, sys.executable, *sys.argv)
            subprocess.Popen([sys.executable] + sys.argv)

    def merge_channels(self):
        """合并多个单通道图像为一个多通道图像"""
        # 打开文件对话框,让用户选择多个图像文件
        file_paths = filedialog.askopenfilenames()
        # 如果用户没有选择文件,直接返回
        if not file_paths:
            return

        # 如果只选择了一个文件,显示错误信息并返回
        if len(file_paths) == 1:
            tk.messagebox.showerror(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['single_file_error']
            )
            return

        try:
            self.image = image_channels.merge_image_channels(file_paths)
        except ValueError as e:
            tk.messagebox.showerror(
                self.language_data[self.current_language]['error'],
                str(e)
            )
            return

        # 显示当前图像
        self.display_image()

    def split_image_channels(self):
        """分割当前图像的通道,并在新窗口中显示每个通道"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_loaded']
            )
            return

        if len(self.image.shape) == 2:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['single_channel_error']
            )
            return

        try:
            channels = image_channels.split_image_channels(self.image)
        except ValueError as e:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                str(e)
            )
            return

        for i, channel in enumerate(channels):
            window = tk.Toplevel(self.master)
            window.title(self.language_data[self.current_language]['channel_title'].format(i))
            canvas = tk.Canvas(window, width=300, height=300)
            canvas.pack()

            # 定义内部函数用于保存单个通道
            def save_channel(channel, index):
                save_path = filedialog.asksaveasfilename(defaultextension=config.DEFAULT_SAVE_FORMAT)
                if not save_path:
                    return
                image_io.save_image(channel, save_path)
                tk.messagebox.showinfo(
                    self.language_data[self.current_language]['success'],
                    self.language_data[self.current_language]['channel_saved'].format(index, save_path)
                )

            save_button = tk.Button(window, text=self.language_data[self.current_language]['save_channel'], command=lambda channel=channel: save_channel(channel, i))
            save_button.pack()

            window.update()

            # 显示子通道图像
            image = Image.fromarray(channel)  # 将通道数据转换为PIL图像对象
            canvas_width = canvas.winfo_width()  # 获取画布的宽度
            canvas_height = canvas.winfo_height()  # 获取画布的高度
            image_width, image_height = image.size  # 获取图像的宽度和高度
            scale = min(canvas_width / image_width, canvas_height / image_height)  # 计算图像的缩放比例
            if scale < 1:  # 如果图像过大,缩小图像
                new_width = int(image_width * scale)
                new_height = int(image_height * scale)
                image = image.resize((new_width, new_height), Image.LANCZOS)  # 使用Lanczos滤波器进行缩小
            else:  # 如果图像过小,放大图像
                new_width = int(image_width * scale)
                new_height = int(image_height * scale)
                image = image.resize((new_width, new_height), Image.BICUBIC)  # 使用双三次滤波器进行放大

            photo = ImageTk.PhotoImage(image)  # 将PIL图像对象转换为PhotoImage对象
            x = (canvas_width - new_width) // 2  # 计算图像在画布上的水平居中位置
            y = (canvas_height - new_height) // 2  # 计算图像在画布上的垂直居中位置
            canvas.create_image(x, y, anchor=tk.NW, image=photo)  # 在画布上显示图像
            canvas.image = photo  # 将PhotoImage对象保存到画布中,防止被垃圾回收

            # 在子图上添加清晰的标题
            title = f"Channel {i}"
            canvas.create_text(canvas_width//2, 15, text=title, font=("Arial", 15), fill="black")

        logging.info("图像分割操作成功。")  # 记录成功日志

    def show_image_details(self):
        """显示当前图像的详细信息"""
        if self.image is None:  # 如果当前没有图像,显示相应消息并返回
            messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_loaded']
            )
            logging.info("没有载入任何图像,无法显示图像细节。")  # 记录日志信息
            return

        # 从路径中提取文件名
        filename = os.path.basename(self.image_path)

        # 计算其他基本信息
        total_pixels = self.image.size  # 获取图像的总像素数
        shape = self.image.shape  # 获取图像的形状(高度,宽度,通道数)
        channels = 1 if len(self.image.shape) == 2 else self.image.shape[2]  # 获取图像的通道数
        dtype = self.image.dtype  # 获取图像像素的数据类型

        # 构建信息字符串,包括文件名
        details = self.language_data[self.current_language]['info_str'].format(filename, total_pixels, shape, channels, dtype)

        # 显示信息
        messagebox.showinfo("INFO", details)
        logging.info("已显示图像细节。")  # 记录成功日志

    def on_mouse_down(self, event):
        """处理鼠标左键按下事件"""
        if self.current_rect and not self.is_drawing:
            # 如果存在矩形框且不是在拖动状态,删除矩形框并重置状态
            self.canvas.delete(self.current_rect)
            self.current_rect = None
        else:
            # 否则,开始绘制新的矩形框
            self.rect_start_x = event.x
            self.rect_start_y = event.y
            self.is_drawing = True

    def on_mouse_move(self, event):
        """处理鼠标左键移动事件"""
        if self.is_drawing:
            if self.current_rect:
                self.canvas.delete(self.current_rect)
            self.current_rect = self.canvas.create_rectangle(
                self.rect_start_x, self.rect_start_y, event.x, event.y, outline="red"
            )

    def on_mouse_up(self, event):
        """处理鼠标左键释放事件"""
        # 确保所有坐标都已设置
        if self.rect_start_x is not None and self.rect_start_y is not None:
            # 标记矩形的结束点并完成绘制
            self.rect_end_x, self.rect_end_y = event.x, event.y
            self.is_drawing = False
            # 保存裁剪区域的坐标
            self.crop_region = (
                min(self.rect_start_y, self.rect_end_y),
                max(self.rect_start_y, self.rect_end_y),
                min(self.rect_start_x, self.rect_end_x),
                max(self.rect_start_x, self.rect_end_x)
            )
            logging.info("已绘制裁剪区域。")  # 记录成功日志

    def Open_as_original(self):
        """打开原始图像"""
        logging.info("正在打开原始图像...")
        self.image_path = filedialog.askopenfilename()  # 打开文件对话框,让用户选择图像文件
        if not self.image_path:
            logging.info("没有选择任何图像文件,打开操作已取消。")  # 记录日志信息
            return  # 如果没有选择任何文件,直接返回
        self.image = image_io.load_image(self.image_path)  # 加载图像文件
        self.display_image()  # 显示图像
        logging.info("成功打开原始图像。")  # 记录成功日志

    def Open_as_gray(self):
        """将原始图像转换为灰度图像"""
        self.image_path = filedialog.askopenfilename()  # 打开文件对话框,让用户选择图像文件
        if not self.image_path:
            logging.info("没有选择任何图像文件,打开操作已取消。")  # 记录日志信息
            return  # 如果没有选择任何文件,直接返回
        self.image = image_io.load_image(self.image_path, as_gray=True)  # 加载图像文件并转换为灰度图像
        self.display_image()  # 显示图像
        logging.info("成功打开并转换为灰度图像。")  # 记录成功日志

    def display_image(self):
        """在主画布上显示当前图像"""
        if self.image is None:
            logging.info("当前没有图像可显示。")  # 记录日志信息
            return  # 如果当前没有图像,直接返回

        image = Image.fromarray(self.image)  # 将numpy数组转换为PIL图像对象

        # 计算图像的缩放比例
        canvas_width = self.canvas.winfo_width()  # 获取画布的宽度
        canvas_height = self.canvas.winfo_height()  # 获取画布的高度
        image_width, image_height = image.size  # 获取图像的宽度和高度
        scale = min(canvas_width / image_width, canvas_height / image_height)  # 计算图像的缩放比例

        # 缩放图像
        if scale < 1:
            # 图像过大,缩小图像
            new_width = int(image_width * scale)
            new_height = int(image_height * scale)
            image = image.resize((new_width, new_height), Image.LANCZOS)  # 使用Lanczos滤波器进行缩小
        else:
            # 图像过小,放大图像
            new_width = int(image_width * scale)
            new_height = int(image_height * scale)
            image = image.resize((new_width, new_height), Image.BICUBIC)  # 使用双三次滤波器进行放大

        photo = ImageTk.PhotoImage(image)  # 将PIL图像对象转换为PhotoImage对象
        self.canvas.delete("all")  # 清除画布上的所有内容

        # 计算图像在画布上的居中位置
        x = (canvas_width - new_width) // 2
        y = (canvas_height - new_height) // 2
        self.canvas.create_image(x, y, anchor=tk.NW, image=photo)  # 在画布上显示图像, tk.NW表示图像的左上角对齐画布的左上角
        self.canvas.image = photo  # 将PhotoImage对象保存到画布中,防止被垃圾回收
        logging.info("成功显示图像。")  # 记录成功日志

    def save_image(self):
        """保存当前图像"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_to_be_saved']
            )
            logging.error("没有图像可保存,保存操作已取消。")  # 记录错误日志
            return  # 如果当前没有图像,直接返回

        save_path = filedialog.asksaveasfilename(defaultextension=config.DEFAULT_SAVE_FORMAT)  # 打开保存文件对话框
        if not save_path:
            logging.info("没有选择保存路径,保存操作已取消。")  # 记录日志信息
            return  # 如果没有选择保存路径,直接返回

        image_io.save_image(self.image, save_path)  # 将当前图像保存到指定路径
        logging.info(f"图像已成功保存到 {save_path}。")  # 记录成功日志

    def crop_image(self):
        """裁剪当前图像"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_to_be_cropped']
            )
            logging.error("没有图像可裁剪,裁剪操作已取消。")  # 记录错误日志
            return  # 如果当前没有图像,直接返回

        # 检查是否有绘制的裁剪区域
        if hasattr(self, 'crop_region') and self.current_rect:
            # 计算实际裁剪区域坐标(考虑图像缩放和偏移)
            image_height, image_width = self.image.shape[:2]  # 获取图像的高度和宽度
            canvas_width = self.canvas.winfo_width()  # 获取画布的宽度
            canvas_height = self.canvas.winfo_height()  # 获取画布的高度
            scale = min(canvas_width / image_width, canvas_height / image_height)  # 计算图像的缩放比例
            offset_x = (canvas_width - image_width * scale) // 2  # 计算图像在画布上的水平偏移量
            offset_y = (canvas_height - image_height * scale) // 2  # 计算图像在画布上的垂直偏移量

            # 计算实际裁剪区域坐标
            real_crop_region = (
                int((self.crop_region[0] - offset_y) / scale),
                int((self.crop_region[1] - offset_y) / scale),
                int((self.crop_region[2] - offset_x) / scale),
                int((self.crop_region[3] - offset_x) / scale)
            )

            cropped_image = image_transform.crop_image(self.image, real_crop_region)  # 裁剪图像
            if cropped_image is not None:
                self.image = cropped_image  # 将裁剪后的图像保存到self.image中
                self.display_image()  # 显示裁剪后的图像
                logging.info("图像裁剪成功。")  # 记录成功日志

                # 清零裁剪框信息
                self.canvas.delete(self.current_rect)  # 删除画布上的裁剪矩形框
                self.current_rect = None  # 将当前裁剪矩形框对象设置为None
                del self.crop_region  # 删除存储裁剪区域坐标的属性
            else:
                tk.messagebox.showinfo(
                    self.language_data[self.current_language]['error'],
                    self.language_data[self.current_language]['crop_failed']
                )
                logging.error("裁剪操作失败。")  # 记录错误日志
        else:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_crop_area']
            )
            logging.error("没有选择裁剪区域,裁剪操作已取消。")

    def reset_image(self):
        """重置当前图像为原始状态"""
        if not self.image_path:  # 如果没有图像路径,显示错误消息并返回
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_reset_image']
            )
            logging.error("没有图像可重置,重置操作已取消。")
            return
        self.image = image_io.load_image(self.image_path)
        self.display_image()
        logging.info("图像已重置为原始状态。")

    def adjust_brightness(self):
        """调整图像亮度"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_loaded']
            )
            return

        # 保存原始图像的副本，用于重置操作
        original_image = self.image.copy()

        # 创建亮度调节窗口
        brightness_window = tk.Toplevel(self.master)
        brightness_window.title(self.language_data[self.current_language]['adjust_brightness'])

        # 设置窗口关闭时的回调函数
        brightness_window.protocol("WM_DELETE_WINDOW", lambda: self.on_brightness_window_close(brightness_window, original_image))

        # 创建拖动条
        brightness_scale = tk.Scale(
            brightness_window,
            from_=0.0,
            to=2.0,
            resolution=0.01,
            orient=tk.HORIZONTAL
        )
        brightness_scale.set(1.0)
        brightness_scale.pack(padx=20, pady=10)

        def update_image():
            """根据当前滑块值更新图像亮度"""
            scale = brightness_scale.get()
            img_np = original_image.astype(float)  # 始终从原始图像开始调整
            img_np = img_np * scale
            img_np = np.clip(img_np, 0, 255).astype(np.uint8)
            self.image = img_np
            self.display_image()

        brightness_scale.config(command=lambda x: update_image())

        # 创建按钮框架
        button_frame = tk.Frame(brightness_window)
        button_frame.pack(fill=tk.X, padx=20, pady=10)

        # 创建应用按钮
        apply_button = tk.Button(button_frame, text=self.language_data[self.current_language]['apply'],
                                 command=lambda: self.on_brightness_window_close(brightness_window, None))
        apply_button.pack(side=tk.LEFT, padx=5)

        # 创建重置按钮
        reset_button = tk.Button(button_frame, text=self.language_data[self.current_language]['reset'],
                                 command=lambda: brightness_scale.set(1.0))
        reset_button.pack(side=tk.LEFT, padx=5)

        # 创建取消按钮
        cancel_button = tk.Button(button_frame, text=self.language_data[self.current_language]['cancel'],
                                 command=lambda: self.on_brightness_window_close(brightness_window, original_image))
        cancel_button.pack(side=tk.LEFT, padx=5)

    def on_brightness_window_close(self, window, original_image=None):
        """处理亮度调节窗口关闭事件"""
        if original_image is not None:  # 如果提供了原始图像，恢复它
            self.image = original_image
            self.display_image()
        window.destroy()

    def add_threshold_menu(self):
        """创建阈值处理菜单"""
        # 创建阈值处理菜单
        threshold_menu = tk.Menu(self.master)

        # 直接使用menubar添加新菜单
        self.menubar.add_cascade(label=self.language_data[self.current_language]['threshold_processing'], menu=threshold_menu)

        # 添加全局阈值处理选项
        threshold_menu.add_command(
            label=self.language_data[self.current_language]['global_threshold'],
            command=self.global_threshold_processing
        )

    def global_threshold_processing(self):
        """全局阈值处理"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_loaded']
            )
            return

        # 检查是否为灰度图像
        if len(self.image.shape) != 2:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['threshold_requires_gray']
            )
            return

        # 保存原始图像的副本，用于重置操作
        original_image = self.image.copy()

        # 创建阈值处理窗口
        threshold_window = tk.Toplevel(self.master)
        threshold_window.title(self.language_data[self.current_language]['global_threshold'])

        # 设置窗口关闭时的回调函数
        threshold_window.protocol("WM_DELETE_WINDOW", lambda: self.on_threshold_window_close(threshold_window, original_image))

        # 创建阈值拖动条 (0-255)
        threshold_scale = tk.Scale(threshold_window, from_=0, to=255, resolution=1, orient=tk.HORIZONTAL,
                                   label=self.language_data[self.current_language]['threshold_value'])
        threshold_scale.set(128)  # 初始阈值设置为128
        threshold_scale.pack(padx=20, pady=10)

        def update_threshold():
            """根据当前滑块值更新阈值处理结果"""
            threshold = threshold_scale.get()
            # 使用numpy数组比较运算实现全局阈值处理
            img_threshold = np.zeros_like(original_image)
            img_threshold[original_image > threshold] = 255  # 大于阈值的像素设为白色
            self.image = img_threshold
            self.display_image()

        threshold_scale.config(command=lambda x: update_threshold())

        # 创建按钮框架
        button_frame = tk.Frame(threshold_window)
        button_frame.pack(fill=tk.X, padx=20, pady=10)

        # 创建应用按钮
        apply_button = tk.Button(button_frame, text=self.language_data[self.current_language]['apply'],
                                 command=lambda: self.on_threshold_window_close(threshold_window, None))
        apply_button.pack(side=tk.LEFT, padx=5)

        # 创建重置按钮
        reset_button = tk.Button(button_frame, text=self.language_data[self.current_language]['reset'],
                                 command=lambda: threshold_scale.set(128))
        reset_button.pack(side=tk.LEFT, padx=5)

        # 创建取消按钮
        cancel_button = tk.Button(button_frame, text=self.language_data[self.current_language]['cancel'],
                                 command=lambda: self.on_threshold_window_close(threshold_window, original_image))
        cancel_button.pack(side=tk.LEFT, padx=5)

    def on_threshold_window_close(self, window, original_image=None):
        """处理阈值窗口关闭事件"""
        if original_image is not None:  # 如果提供了原始图像，恢复它
            self.image = original_image
            self.display_image()
        window.destroy()

    def add_edge_menu(self):
        """创建边缘处理菜单"""
        edge_menu = tk.Menu(self.master)
        self.menubar.add_cascade(label=self.language_data[self.current_language]['edge_processing'], menu=edge_menu)
        edge_menu.add_command(label=self.language_data[self.current_language]['moving_difference'], command=self.moving_difference)

    def moving_difference(self):
        """移动差分处理"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_loaded']
            )
            return

        # 检查是否为灰度图像
        if len(self.image.shape) != 2:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['threshold_requires_gray']
            )
            return

        # 保存原始图像的副本，用于重置操作
        original_image = self.image.copy()

        # 创建移动差分窗口
        difference_window = tk.Toplevel(self.master)
        difference_window.title(self.language_data[self.current_language]['moving_difference'])

        # 设置窗口关闭时的回调函数
        difference_window.protocol("WM_DELETE_WINDOW", lambda: self.on_difference_window_close(difference_window, original_image))

        # 创建拖动条 (-3 到 3)
        difference_scale = tk.Scale(
            difference_window,
            from_=-3,
            to=3,
            resolution=1,
            orient=tk.HORIZONTAL
        )
        difference_scale.set(0)
        difference_scale.pack(padx=20, pady=10)

        def update_difference():
            """根据当前滑块值更新移动差分结果"""
            shift = difference_scale.get()
            shifted_image = np.roll(original_image, shift, axis=1)
            if shift > 0:
                shifted_image[:, :shift] = 0
            elif shift < 0:
                shifted_image[:, shift:] = 0
            diff_image = np.abs(original_image - shifted_image).astype(np.uint8)
            self.image = diff_image
            self.display_image()

        difference_scale.config(command=lambda x: update_difference())

        # 创建按钮框架
        button_frame = tk.Frame(difference_window)
        button_frame.pack(fill=tk.X, padx=20, pady=10)

        # 创建应用按钮
        apply_button = tk.Button(button_frame, text=self.language_data[self.current_language]['apply'],
                                 command=lambda: self.on_difference_window_close(difference_window, None))
        apply_button.pack(side=tk.LEFT, padx=5)

        # 创建重置按钮
        reset_button = tk.Button(button_frame, text=self.language_data[self.current_language]['reset'],
                                 command=lambda: difference_scale.set(0))
        reset_button.pack(side=tk.LEFT, padx=5)

        # 创建取消按钮
        cancel_button = tk.Button(button_frame, text=self.language_data[self.current_language]['cancel'],
                                 command=lambda: self.on_difference_window_close(difference_window, original_image))
        cancel_button.pack(side=tk.LEFT, padx=5)

    def on_difference_window_close(self, window, original_image=None):
        """处理移动差分窗口关闭事件"""
        if original_image is not None:  # 如果提供了原始图像，恢复它
            self.image = original_image
            self.display_image()
        window.destroy()

    def add_mask_menu(self):
        """创建掩膜处理菜单"""
        mask_menu = tk.Menu(self.menubar)
        self.menubar.add_cascade(label=self.language_data[self.current_language]['create_mask'], menu=mask_menu)
        mask_menu.add_command(
            label=self.language_data[self.current_language]['red_mask'],
            command=lambda: self.create_mask('red')
        )
        mask_menu.add_command(
            label=self.language_data[self.current_language]['green_mask'],
            command=lambda: self.create_mask('green')
        )
        mask_menu.add_command(
            label=self.language_data[self.current_language]['blue_mask'],
            command=lambda: self.create_mask('blue')
        )

    def create_mask(self, color):
        """创建颜色掩膜"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_loaded']
            )
            return

        # 确保图像为 RGB 格式
        if len(self.image.shape) == 2:  # 灰度图像
            original_shape = self.image.shape
            self.image = np.stack([self.image] * 3, axis=2)
            logging.info(f"将单通道图像 {original_shape} 转换为 RGB 格式 {self.image.shape}")
        elif len(self.image.shape) == 3 and self.image.shape[2] == 4:  # RGBA 图像
            original_shape = self.image.shape
            self.image = self.image[:, :, :3]  # 去掉 alpha 通道
            logging.info(f"将 RGBA 图像 {original_shape} 转换为 RGB 格式 {self.image.shape}")

        # 检查图像通道数
        if len(self.image.shape) != 3 or self.image.shape[2] != 3:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['mask_requires_rgb']
            )
            return

        # 将图像从 RGB 转换为 HSV 颜色空间
        hsv_image = cv2.cvtColor(self.image, cv2.COLOR_RGB2HSV)

        # 定义颜色范围
        if color == 'red':
            lower_red1 = np.array([0, 50, 50])
            upper_red1 = np.array([15, 255, 255])
            lower_red2 = np.array([165, 50, 50])
            upper_red2 = np.array([180, 255, 255])
            mask1 = cv2.inRange(hsv_image, lower_red1, upper_red1)
            mask2 = cv2.inRange(hsv_image, lower_red2, upper_red2)
            mask = cv2.bitwise_or(mask1, mask2)
            title = self.language_data[self.current_language]['red_mask']
        elif color == 'green':
            lower_green = np.array([40, 100, 100])
            upper_green = np.array([80, 255, 255])
            mask = cv2.inRange(hsv_image, lower_green, upper_green)
            title = self.language_data[self.current_language]['green_mask']
        elif color == 'blue':
            lower_blue = np.array([100, 100, 100])
            upper_blue = np.array([130, 255, 255])
            mask = cv2.inRange(hsv_image, lower_blue, upper_blue)
            title = self.language_data[self.current_language]['blue_mask']
        else:
            return

        # 将单通道掩膜转换为三通道
        mask = np.stack([mask] * 3, axis=2)

        # 创建掩膜显示窗口和 Canvas
        mask_window = tk.Toplevel(self.master)
        mask_window.title(title)
        canvas = tk.Canvas(mask_window, width=500, height=400)
        canvas.pack()

        # 使用 update 确保窗口尺寸信息已更新
        mask_window.update()

        # 显示掩膜图像
        mask_img = Image.fromarray(mask)
        image_width, image_height = mask_img.size
        logging.info(f"原始图像尺寸: {image_width} x {image_height}")

        canvas_width = canvas.winfo_width()
        canvas_height = canvas.winfo_height()
        logging.info(f"Canvas 尺寸: {canvas_width} x {canvas_height}")

        scale = min(canvas_width / image_width, canvas_height / image_height)
        if scale > 1:
            scale = 1.0

        new_width = max(1, int(image_width * scale))
        new_height = max(1, int(image_height * scale))
        logging.info(f"新图像尺寸: {new_width} x {new_height}")

        try:
            mask_img = mask_img.resize((new_width, new_height), Image.BICUBIC)
        except Exception as e:
            logging.error(f"图像缩放失败: {e}")
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                f"图像缩放失败: {str(e)}"
            )
            return

        try:
            photo = ImageTk.PhotoImage(mask_img)
            x = max(0, (canvas_width - new_width) // 2)
            y = max(0, (canvas_height - new_height) // 2)
            canvas.create_image(x, y, anchor=tk.NW, image=photo)
            canvas.image = photo  # 防止被垃圾回收
            logging.info("成功显示掩膜图像")
        except Exception as e:
            logging.error(f"显示图像失败: {e}")
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                f"显示图像失败: {str(e)}"
            )
            return

        # 保存掩膜按钮
        def save_mask():
            save_path = filedialog.asksaveasfilename(defaultextension=config.DEFAULT_SAVE_FORMAT)
            if save_path:
                image_io.save_image(mask, save_path)
                tk.messagebox.showinfo(
                    self.language_data[self.current_language]['success'],
                    self.language_data[self.current_language]['mask_saved'].format(save_path)
                )

        save_button = tk.Button(mask_window, text=self.language_data[self.current_language]['save_mask'], command=save_mask)
        save_button.pack(pady=10)

        mask_window.update_idletasks()
        logging.info(f"{title} 窗口已创建")

    def add_bitwise_menu(self):
        """创建位运算主菜单"""
        bitwise_menu = tk.Menu(self.menubar)
        self.menubar.add_cascade(label=self.language_data[self.current_language]['bitwise_operations'], menu=bitwise_menu)
        bitwise_menu.add_command(
            label=self.language_data[self.current_language]['bitwise_and'],
            command=self.bitwise_and_operation
        )
        bitwise_menu.add_command(
            label=self.language_data[self.current_language]['bitwise_or'],
            command=lambda: messagebox.showinfo("Info", "该功能暂未实现。")
        )
        bitwise_menu.add_command(
            label=self.language_data[self.current_language]['bitwise_xor'],
            command=lambda: messagebox.showinfo("Info", "该功能暂未实现。")
        )
        bitwise_menu.add_command(
            label=self.language_data[self.current_language]['bitwise_not'],
            command=self.bitwise_not_operation
        )

    def bitwise_and_operation(self):
        """与运算（遮罩）功能"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_loaded']
            )
            return

        # 选择遮罩图像
        mask_path = filedialog.askopenfilename()
        if not mask_path:
            return

        # 加载遮罩图像
        mask = image_io.load_image(mask_path, as_gray=True)
        if mask is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['invalid_mask']
            )
            return
        # 调整遮罩图像大小以匹配当前图像
        if mask.shape[:2] != self.image.shape[:2]:
            mask = cv2.resize(mask, (self.image.shape[1], self.image.shape[0]))

        # 将单通道遮罩转换为三通道
        if len(mask.shape) == 2:
            mask = np.stack([mask] * 3, axis=2)

        # 进行与运算
        result = cv2.bitwise_and(self.image, mask)
    
        # 更新当前图像
        self.image = result
        self.display_image()
        logging.info("与运算（遮罩）操作成功。")
        result = cv2.bitwise_and(self.image, self.image, mask=mask)
    
    def bitwise_not_operation(self):
        """非运算（反转）功能"""
        if self.image is None:
            tk.messagebox.showinfo(
                self.language_data[self.current_language]['error'],
                self.language_data[self.current_language]['no_image_loaded']
            )
            return

        # 检查图像是否为 RGBA 格式（有 4 个通道）
        if len(self.image.shape) == 3 and self.image.shape[2] == 4:
            # 分离 RGB 通道和 Alpha 通道
            rgb_channels = self.image[:, :, :3]
            alpha_channel = self.image[:, :, 3]

            # 对 RGB 通道进行非运算
            rgb_not = cv2.bitwise_not(rgb_channels)

            # 重新组合 RGB 通道和 Alpha 通道
            result = np.dstack((rgb_not, alpha_channel))
        else:
            # 对非RGBA图像进行非运算
            result = cv2.bitwise_not(self.image)

        # 更新当前图像并显示
        self.image = result
        self.display_image()
        logging.info("非运算（反转）操作成功。")


if __name__ == "__main__":
    root = tk.Tk()
    app = ImageProcessor(root)
    root.mainloop()